package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.nursing.domain.pojo.AlertData;
import com.zzyl.nursing.domain.pojo.AlertRule;
import com.zzyl.nursing.domain.pojo.DeviceData;
import com.zzyl.nursing.domain.vo.AlertNotifyVo;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertHandleService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.nursing.ws.WebSocketServer;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
@Slf4j
public class AlertHandleServiceImpl implements IAlertHandleService {
    private final IAlertRuleService alertRuleService;
    private final StringRedisTemplate stringRedisTemplate;
    private final DeviceMapper deviceMapper;
    private final SysUserRoleMapper sysUserRoleMapper;
    private final IAlertDataService alertDataService;
    private final WebSocketServer webSocketServer;


    /**
     * 一、获取所有设备的最新指标数据List<DeviceDataVo>
     * <p>
     * 1.查询所有启用的规则：主要目的是判断，如果没有规则则不需要检测异常数据<br>
     * 2.获取所有设备的最新指标数据合并成一个大集合：List<DeviceData><br>
     * 3.遍历List<DeviceData>然后对每一个指标数据进行检测<br>
     * </p>
     */
    @Override
    //练习
    public void alertFilter() {
        log.info("报警数据诊断执行");
        // 1.查询所有启用的规则：主要目的是判断，如果没有规则则不需要检测异常数据
        Long deviceDateCount = alertRuleService.lambdaQuery().eq(AlertRule::getStatus, 1)
                .count();
        if(deviceDateCount <=0){
            return;
        }


        // 2.获取所有设备的最新指标数据合并成一个大集合：List<DeviceDataVo>
        //2.1从redis中拿到最新的上报数据
        List<Object> allDeviceDatasJsonList = stringRedisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if (CollUtil.isEmpty(allDeviceDatasJsonList)) {
            return;
        }
        // 最终要构成的一个大集合
        //将最新数据封装到DeviceData中
        ArrayList<DeviceData> deviceDateList = new ArrayList<>();
        allDeviceDatasJsonList.forEach(deviceDatasJsonList->{
            deviceDateList.addAll(JSONUtil.toList(deviceDatasJsonList.toString(),DeviceData.class));
        });

        // 3.遍历List<DeviceDataVo>然后对每一个指标数据进行检测
        deviceDateList.forEach(this::findAllAlertRole);

    }
    //跟着老师
//    @Override
//    public void alertFilter() {
//        log.info("报警数据诊断执行");
//        // 1.查询所有启用的规则：主要目的是判断，如果没有规则则不需要检测异常数据
//        Long alertCount = alertRuleService.lambdaQuery().eq(AlertRule::getStatus, 1)
//                .count();
//        if(alertCount <= 0){
//            return;
//        }
//
//
//        // 2.获取所有设备的最新指标数据合并成一个大集合：List<DeviceDataVo>
//        //2.1从redis中拿到最新的上报数据
//        List<Object> deviceDateJsonList = stringRedisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
//        if (CollUtil.isEmpty(deviceDateJsonList)) {
//            return;
//        }
//
//        ArrayList<DeviceData> deviceDateList = new ArrayList<>();
//        // 最终要构成的一个大集合
//        //将最新数据封装到DeviceData中
//        deviceDateJsonList.forEach(deviceDates->{
//            deviceDateList.addAll(JSONUtil.toList(deviceDates.toString(),DeviceData.class));
//        });
//
//        // 3.遍历List<DeviceDataVo>然后对每一个指标数据进行检测
//        deviceDateList.forEach(this::findAllAlertRole);
//    }
    //预习
//    @Override
//    public void alertFilter() {
//        log.info("报警数据诊断执行");
//        // 1.查询所有启用的规则：主要目的是判断，如果没有规则则不需要检测异常数据
//        Long alertRuleCount = alertRuleService
//                .lambdaQuery()
//                .eq(AlertRule::getStatus, 1)
//                .count();
//
//        if (alertRuleCount <= 0) {
//            return;
//        }
//
//
//        // 2.获取所有设备的最新指标数据合并成一个大集合：List<DeviceDataVo>
//        //2.1从redis中拿到最新的上报数据
//        List<Object> allDeviceDataJsonStrList = stringRedisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
//        if (ObjectUtil.isEmpty(allDeviceDataJsonStrList)) {
//            return;
//        }
//
//        // 最终要构成的一个大集合
//        List<DeviceData> deviceDataList = new ArrayList<>();
//        //将最新数据封装到DeviceData中
//        allDeviceDataJsonStrList.forEach(jsonStrList -> {
//            deviceDataList.addAll(JSONUtil.toList(jsonStrList.toString(),DeviceData.class));
//        });
//
//        // 3.遍历List<DeviceDataVo>然后对每一个指标数据进行检测
//        for (DeviceData deviceData : deviceDataList) {
//            findAllAlertRole(deviceData);
//        }
//    }

    /**
     * 获取规则表中的所有规则
     * 判断当前传递过来的属性数据是否一致
     * 从表中获取所有的规则:状态为启用:1
     * 对规则就行遍历:拿每一个规则属性就行比对
     */
    //练习
    private void findAllAlertRole(DeviceData deviceData) {
    // 1.判断指标数据是否是最新的数据：是否在一分钟内（基于当前定时任务程序）
        LocalDateTime alarmTime = deviceData.getAlarmTime();
        // 返回一个 Duration 对象，表示从 alarmTime 到当前时间的时间间隔。
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        //不在一分钟内则不处理
//        if (between>60) {
//            return;
//        }
        // 2.查询该指标的所有规则数据List<AlertRule>：
        // 查询所有的该产品下的规则
        List<AlertRule> allDeviceAlertRules = alertRuleService.lambdaQuery()
                .eq(AlertRule::getIotId, "-1")
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getStatus, 1)
                .list();
        // 查询指定设备的规则
        List<AlertRule> currentDeviceAlertRules = alertRuleService.lambdaQuery()
                .eq(AlertRule::getIotId, deviceData.getIotId())
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getStatus, 1)
                .list();
        //将指定设备规则融入所有设备规则中
        CollUtil.addAllIfNotContains(allDeviceAlertRules,currentDeviceAlertRules);
        //对所有报警规则进行遍历
        allDeviceAlertRules.forEach(deviceAlertRule ->{
            compareHandle(deviceAlertRule,deviceData);
        });

    }
    //预习
//    private void findAllAlertRole(DeviceData deviceData) {
//    // 1.判断指标数据是否是最新的数据：是否在一分钟内（基于当前定时任务程序）
//        LocalDateTime alarmTime = deviceData.getAlarmTime();
//        //返回一个 Duration 对象，表示从 alarmTime 到当前时间的时间间隔。
//        Duration duration = Duration.between(alarmTime, LocalDateTime.now());
//        if(duration.toSeconds()<60){
//            return;
//        }
//        // 2.查询该指标的所有规则数据List<AlertRule>：
//        // 查询所有的该产品下的规则
//        List<AlertRule> alertRuleList = alertRuleService.<AlertRule>lambdaQuery()
//                .eq(AlertRule::getProductKey, deviceData.getProductKey())
//                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
//                .eq(AlertRule::getIotId, "-1")
//                .eq(AlertRule::getStatus, 1)
//                .list();
//        // 查询指定设备的规则
//        List<AlertRule> iotList = alertRuleService.<AlertRule>lambdaQuery()
//                .eq(AlertRule::getProductKey, deviceData.getProductKey())
//                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
//                .eq(AlertRule::getIotId, deviceData.getIotId())
//                .eq(AlertRule::getStatus, 1).list();
//        Collection<AlertRule> alertRules = CollUtil.addAll(alertRuleList, iotList);
//        for (AlertRule alertRule : alertRules) {
//            compareHandle(alertRule,deviceData);
//        }
//    }

    /**
     * 那每一个规则和传来的属性比对
     * 判断:属性数据中存在规则定义中定义有效时间
     * 比对:属性数据dataValue和规则中阈值以及比较符进行比对
     * 如果数据异常:从redis中查询是否有报警过(有则不报,无则报警)
     * 如果是异常数据却没有报警,从redis中查询异常数据次数是否达到次数
     * 一旦确定需要报警,查询可以处理的人
     * websocker通知对应的报警信息
     */
    //练习
    private void compareHandle(AlertRule alertRule, DeviceData deviceData) {
        log.info("规则比较");
        // 1.判断上报时间是否在规则中定义的有效时间内：00:00:00~23:59:59
        String[] split = alertRule.getAlertEffectivePeriod().split("~");
        LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();
        LocalTime startLocalDataTime = LocalDateTimeUtil.parse(split[0], DateTimeFormatter.ofPattern("HH:mm:ss")).toLocalTime();
        LocalTime endLocalDataTime = LocalDateTimeUtil.parse(split[1], DateTimeFormatter.ofPattern("HH:mm:ss")).toLocalTime();

        if(alarmTime.isBefore(startLocalDataTime) || alarmTime.isAfter(endLocalDataTime)){
            return;
        }
        // 2.检测指标数据是否达到规则中阈值：HeartRate < 65
        // 指标数据 60
        String operator = alertRule.getOperator();
        float value = alertRule.getValue();
        float dataValue =Float.valueOf( deviceData.getDataValue());


        //规则是65
        String adnormlDeviceDataCountKey = CacheConstants.IOT_COUNT_ALERT + deviceData.getIotId() + ":" + alertRule.getId();
        int compare = Float.compare(dataValue, value);
        String adnormalSilentAlertCountKey = CacheConstants.IOT_SILENT_ALERT +  deviceData.getIotId() + ":" + alertRule.getId();
        if(compare >=0 && operator.equals(">=") || compare < 0 && operator.equals("<")){
            if (stringRedisTemplate.hasKey(adnormalSilentAlertCountKey)) {
                log.info("该设备已经报警,无需重复,{}分钟后重新报警",alertRule.getAlertSilentPeriod());
                return;
            }

            String adnormalDeviceDateAlertStr = stringRedisTemplate.opsForValue().get(adnormlDeviceDataCountKey);
            int adnormalDeviceDateAlertCount = StrUtil.isNotEmpty(adnormalDeviceDateAlertStr) ? Integer.parseInt(adnormalDeviceDateAlertStr) : 0;
            Integer duration = alertRule.getDuration();
            if (adnormalDeviceDateAlertCount + 1 < duration) {
                stringRedisTemplate.opsForValue().increment((adnormlDeviceDataCountKey));
                return;
            }
            log.info("异常数据已经连续{}次检测异常数据,此时需要触发报警",duration);
            //删除redis中的异常数据统计个数
            stringRedisTemplate.delete(adnormlDeviceDataCountKey);
            stringRedisTemplate.opsForValue().set(adnormalSilentAlertCountKey,"true",alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);
            //todo 通知人
            List<Long> userIdList = saveBatchAlertData(deviceData, alertRule);

           sendMessageNotify( alertRule, deviceData, userIdList) ;
        }else {
            stringRedisTemplate.delete(adnormlDeviceDataCountKey);

        }

        //操作符

        // 3.判断是否已报警：redis中判断是否存在该key=alert:silent:cycle:{iotId}:{ruleId}



    }
    //跟着老师练习
//        private void compareHandle(AlertRule alertRule, DeviceData deviceData) {
//
//        // 1.判断上报时间是否在规则中定义的有效时间内：00:00:00~23:59:59
//
//
//        LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();
//        String alertEffectivePeriod = alertRule.getAlertEffectivePeriod();
//        LocalTime startLocalTime = LocalDateTimeUtil.parse(alertEffectivePeriod.split("~")[0], DateTimeFormatter.ofPattern("HH:mm:ss")).toLocalTime();
//        LocalTime endLocalTime = LocalDateTimeUtil.parse(alertEffectivePeriod.split("~")[1], DateTimeFormatter.ofPattern("HH:mm:ss")).toLocalTime();
//        if(alarmTime.isBefore(startLocalTime) || alarmTime.isAfter(endLocalTime) ){
//            return;
//        }
//        // 2.检测指标数据是否达到规则中阈值：HeartRate < 65
//        // 指标数据 60
//
//
//        float functionIdDataValue =Float.parseFloat( deviceData.getDataValue());
//        String operator = alertRule.getOperator();
//        float alertRuleValue = alertRule.getValue();
//        //规则是65
//
//
//        int compare = Float.compare(functionIdDataValue, alertRuleValue);
//        String adnormlDeviceDataCountKey = CacheConstants.IOT_COUNT_ALERT + deviceData.getIotId() + ":" + alertRule.getId();
//        if((compare >= 0 && operator.equals(">=") )|| (compare < 0 && operator.equals("<")) ){
//
//        }else {
//            stringRedisTemplate.opsForHash().delete(adnormlDeviceDataCountKey);
//        }
//        //操作符
//
//        // 3.判断是否已报警：redis中判断是否存在该key=alert:silent:cycle:{iotId}:{ruleId}
//
//
//
//    }




    //预习
//    private void compareHandle(AlertRule alertRule, DeviceData deviceData) {
//        // 1.判断上报时间是否在规则中定义的有效时间内：00:00:00~23:59:59
//        LocalTime alertTime = deviceData.getAlarmTime().toLocalTime();
//        LocalTime startLocalTime = LocalTime.parse(alertRule.getAlertEffectivePeriod().split("~")[0], DateTimeFormatter.ofPattern("HH:mm:ss"));
//        LocalTime endLocalTime = LocalTime.parse(alertRule.getAlertEffectivePeriod().split("~")[1], DateTimeFormatter.ofPattern("HH:mm:ss"));
//        if(alertTime.isBefore(startLocalTime) || alertTime.isAfter(endLocalTime)){
//            return;
//        }
//        // 2.检测指标数据是否达到规则中阈值：HeartRate < 65
//        // 指标数据 60
//        float dataValue = Float.parseFloat(deviceData.getDataValue());
//        //规则是65
//        float value = alertRule.getValue();
//        //操作符
//        String operator = alertRule.getOperator();
//        int compare = Float.compare(dataValue, value);
//        String alertTriggerCountRedisKey = CacheConstants.IOT_COUNT_ALERT + alertRule.getIotId();
//        if((compare < 0 && StrUtil.equals(operator,"<")) || (compare > 0 && StrUtil.equals(operator,">")))
//        {
//            log.info("检测出数据异常alert,alert,alert,alert,alert,alert,alert");
//        }else {
//            stringRedisTemplate.delete(alertTriggerCountRedisKey);
//            return;
//        }
//        // 3.判断是否已报警：redis中判断是否存在该key=alert:silent:cycle:{iotId}:{ruleId}
//    }

    /**
     * 找报警通知人员批量保存异常数据
     * 找要通知的人员
     * 有多少就构建几个报警数据,并且批量保存
     */
    private List<Long> saveBatchAlertData(DeviceData deviceData, AlertRule alertRule) {
        Integer physicalLocationType = deviceData.getPhysicalLocationType();
        List<Long> notifyUserIds = new ArrayList<>();
        switch (physicalLocationType) {
            case -1:
               notifyUserIds = deviceMapper.selectNursingIdByIotIdWithElder(deviceData.getIotId());
               break;
            case 1:
                notifyUserIds = sysUserRoleMapper.selectUserIdByRoleName("维修工");
                break;
            case 2:
                notifyUserIds  = deviceMapper.selectNursingIdByIotIdWithBed(deviceData.getIotId());
        }
        ArrayList<AlertData> alertDataList = new ArrayList<>();
        //随便送欸超级管理员
        notifyUserIds.addAll(sysUserRoleMapper.selectUserIdByRoleName("超级管理员"));
        notifyUserIds.forEach(notifyUserId ->{
            AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
            //填充其他字段
            alertData.setAlertRuleId(alertRule.getId());
            alertData.setType(alertRule.getAlertDataType());
            String alertReason = StrUtil.format("设备{},出现异常:{}{}{},连续出现{}检查异常,请您及时处理", deviceData.getDeviceName(), alertRule.getFunctionId(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
            alertData.setStatus(0);
            alertData.setAlertReason(alertReason);
            alertData.setUserId(notifyUserId);
            alertDataList.add(alertData);
        });
        alertDataService.saveBatch(alertDataList);
        return notifyUserIds;
    }

    /**
     * 短信通知
     * 构建消息通知模板
     * 使用websocket发送信息
     */
    private void sendMessageNotify(AlertRule alertRule,DeviceData deviceData,List<Long> userIdList) {
        AlertNotifyVo alertNotifyVo = new AlertNotifyVo();
        BeanUtil.copyProperties(deviceData,alertNotifyVo);
        alertNotifyVo.setFunctionName(alertRule.getFunctionName());
        alertNotifyVo.setAlertDataType(alertRule.getAlertDataType());
        alertNotifyVo.setVoiceNotifyStatus(0);
        alertNotifyVo.setNotifyType(1);
        webSocketServer.sendMessageToConsumer(alertNotifyVo,userIdList);
    }
}